/* sprite-sheet.vala
 *
 * Copyright (C) 2010, 2011  Richard Talbot-Watkins
 * Copyright (C) 2010, 2011  Dave Jeffery
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Authors:
 *  Richard Talbot-Watkins <richtw1@gmail.com>
 * 	Dave Jeffery <david.richard.jeffery@gmail.com>
 */

using Gee;

/**
 * This class is the data model for the SpriteSheetWindow class.
 *
 * @author Richard Talbot-Watkins <richtw1@gmail.com>
 * @author Dave Jeffery <david.richard.jeffery@gmail.com>
 * @version 2011.0103
 * @since 0.1
 */
public class BeebSpriter.SpriteSheet : GLib.Object {
	/**
	 * Signal emitted when background colour changed.
	 */
	public signal void background_changed ();

	/**
	 * Signal emitted when a sprite has changed.
	 */
	public signal void sprite_changed ();

	/**
	 * Signal emitted when a sprite sheet order has changed.
	 */
	public signal void order_changed ();

	/**
	 * Signal emitted when one of the sprite sheet export settings has changed.
	 */
	public signal void settings_changed ();

	/* Stores the possible background colours for the sprite sheet widget.*/
	public enum BackColour {
		THEME_DEFAULT,
		BLACK,
		RED,
		GREEN,
		YELLOW,
		BLUE,
		MAGENTA,
		CYAN,
		WHITE;

		/**
		 * Standard to_string function.
		 *
		 * @return The .bspr XML colour name for the layout.
		 */
		public string to_string () {
			switch (this) {
				case BLACK: return "Black";
				case RED: return "Red";
				case GREEN: return "Green";
				case YELLOW: return "Yellow";
				case BLUE: return "Blue";
				case MAGENTA: return "Magenta";
				case CYAN: return "Cyan";
				case WHITE: return "White";
				case THEME_DEFAULT: return "Transparent";
				default: return "Unknown";
			}
		}
	}

	/* Contains the number of one of the BBC Micro/Electron graphics modes.*/
	private int _mode;

	/* Getter/Setter for the mode property.*/
	public int mode {
		get {
			return _mode;
		}

		private set {
			_mode = value;
			y_scale = 2;

			switch (_mode) {
				case 0:
					x_scale = 1;
					bits_per_pixel = 1;
					break;
				case 1:
					x_scale = 2;
					bits_per_pixel = 2;
					break;
				case 2:
					x_scale = 4;
					bits_per_pixel = 4;
					break;
				case 4:
					x_scale = 2;
					bits_per_pixel = 1;
					break;
				case 5:
					x_scale = 4;
					bits_per_pixel = 2;
					break;
			}
		}
	}

	/* Horizontal pixel scaling for the current BBC/Electron screen mode.*/
	public int x_scale { get; private set; }

	/* Vertical pixel scaling for the current BBC/Electron screen mode. */
	public int y_scale { get; private set; }

	/* The number of bits per pixel for the current BBC/Electron mode. */
	public int bits_per_pixel { get; private set; }

	/* List of sprites on the sprite list.*/
	private ArrayList<Sprite> _sprite_list = new ArrayList<Sprite> ();

	/**
	 * This function ensures that a sprite's changed signals are handled
	 * by the sprite sheet whenever a sprite is added to it.
	 *
	 * @param sprite The sprite being added to the sprite sheet.
	 */
	public void add_sprite (Sprite sprite_instance) {
		/* Add sprite. */
		_sprite_list.add (sprite_instance);

		/* Connect signals. */
		sprite_instance.changed.connect (on_sprite_changed);

		/* Emit changed signal. */
		order_changed ();
	}

	/**
	 * This function returns a copy of the sprite sheet's current sprite list.
	 */
	public ArrayList<Sprite> get_sprite_list () {
		var copy = new ArrayList<Sprite> ();
		foreach (var s in _sprite_list) {
			copy.add (s);
		}
		return copy;
	}

	/**
	 * List of the sprite names in the current sprite list.
	 *
	 * @return An string array containing sprite names in the sprite sheet.
	 */
	public string[] get_sprite_names () {
		string[] name_list = {};
		foreach (var s in _sprite_list) {
			name_list += s.name;
		}
		return name_list;
	}

	/**
	 * Called when a sprite in the sprite sheet has been changed.
	 */
	public void on_sprite_changed () {
		/* Emit changed signal. */
		sprite_changed ();
	}

	/**
	 * Called when the sprite order on the sprite sheet has been changed.
	 */
	public void update_order (ArrayList<Sprite> new_order) {
		if (new_order.size != _sprite_list.size) {
			_sprite_list = new_order;
			order_changed ();
			return;
		}

		for (var i = 0; i != new_order.size; ++i) {
			if (new_order.get (i) != _sprite_list.get (i)) {
				_sprite_list = new_order;
				/* Emit changed signal. */
				order_changed ();
				break;
			}
		}
	}

	/**
	 * Background colour of the SpriteSheetWindow SpriteSheet widget.
	 */
	private BackColour _background_colour;

	/**
	 * Getter/Setter for the background_colour property.
	 */
	public BackColour background_colour { 
		get {
			return _background_colour;
		}

		set {
			if (_background_colour != value) {
				_background_colour = value;

				/* Emit changed signal. */
				background_changed ();
			}
		}
	}

	/* Default palette for the sprite sheet's BBC Micro screen mode. */
	public BeebPalette.Colour[] default_palette { get; set; }

	/* Whether to show grid lines at (BBC/Electron) pixel intervals. */
	public bool default_show_grid_lines { get; set; default = true; }

	/* The interval for major horizontal grid lines. */
	public int horizontal_block_dividers { get; set; default = 0; }

	/* The interval for major vertical grid lines. */
	public int vertical_block_dividers { get; set; default = 0; }

	/* Default amount of zoom in sprite editing window. */
	public int default_zoom { get; set; default = 6; }

	/* Members relating to export options. */

	/**
	 * BBC Micro/Electron sprite byte arrangements.
	 *
	 * ROW_MAJOR is best for sprites displayed at character block positions.
	 * COLUMN_MAJOR is best for sprites displayed on any pixel line.
	 */
	public enum SpriteDataLayout {
		ROW_MAJOR,
		COLUMN_MAJOR;

		/**
		 * Standard to_string function.
		 *
		 * @return The .bspr XML name for the layout.
		 */
		public string to_string () {
			return (this == ROW_MAJOR) ? "RowMajor" : "ColumnMajor";
		}
	}

	/* Whether the user has entered export setting for sprite sheet. */
	public bool has_export_settings { get; set; default = false; }

	/**
	 * Determines how the bytes in exported sprites are arranged. 
	 */
	private SpriteDataLayout _sprite_layout = SpriteDataLayout.ROW_MAJOR;

	/**
	 * Getter/Setter for the sprite layout property.
	 */
	public SpriteDataLayout sprite_layout { 
		get {
			return _sprite_layout;
		}
 
		set {
			if (_sprite_layout != value) {
				_sprite_layout = value;

				/* Emit changed signal. */
				settings_changed ();
			}
		} 
	}

	/**
	 * Whether to break up sprites into smaller pieces for export. 
	 */
	private bool _should_break_sprites = false;

	/**
	 * Getter/Setter for the should break sprites property.
	 */
	public bool should_break_sprites { 
		get {
			return _should_break_sprites;
		}

		set {
			if (_should_break_sprites != value) {
				_should_break_sprites = value;

				/* Emit changed signal. */
				settings_changed ();
			}
		}
	}

	/* Width to break sprites into for export (in BBC character blocks). */
	private int _sub_sprite_width = 2;

	/**
	 * Getter/Setter for the sub-sprite width property.
	 */
	public int sub_sprite_width {
		get {
			return _sub_sprite_width;
		}

		set {
			if (_sub_sprite_width != value) {
				_sub_sprite_width = value;

				/* Emit changed signal. */
				settings_changed ();
			}
		}
	}

	/* Height to break sprites into for export (in BBC character blocks). */
	private int _sub_sprite_height = 2;

	/**
	 * Getter/Setter for the sub-sprite height property.
	 */
	public int sub_sprite_height {
		get {
			return _sub_sprite_height;
		}

		set {
			if (_sub_sprite_height != value) {
				_sub_sprite_height = value;

				/* Emit changed signal. */
				settings_changed ();
			}
		}
	}


	/**
	 * Whether to export sprite sheet in two batches, with masks second.
	 */
	private bool _should_export_separate_mask = false;

	/**
	 * Getter/Setter for the should export separate mask property.
	 */
	public bool should_export_separate_mask {
		get {
			return _should_export_separate_mask;
		}
		set {
			if (_should_export_separate_mask != value) {
				_should_export_separate_mask = value;

				/* Emit changed signal. */
				settings_changed ();
			}
		}
	}

	/**
	 * Whether to create a header file for e.g. BeebASM or P65 assembler. 
	 */
	private bool _should_generate_header = true;

	/**
	 * Getter/Setter for the should generate header property.
	 */
	public bool should_generate_header {
		get {
			return _should_generate_header;
		}

		set {
			if (_should_generate_header != value) {
				_should_generate_header = value;

				/* Emit changed signal. */
				settings_changed ();
			}
		} 
	}

	/**
	 * The syntax for the header file. The default is for BeebASM. 
	 */
	private string _assembler_syntax = "{n} = &{v}";

	/**
	 * Getter/Setter for the assembler header file syntax property.
	 */
	public string assembler_syntax {
		get {
			return _assembler_syntax;
		}

		set {
			if (_assembler_syntax != value) {
				_assembler_syntax = value;

				/* Emit changed signal. */
				settings_changed ();
			}
		} 
	}

	/* Number of BBC logical colours used on current sprite sheet. */
	public int num_colours { 
		get {
			return 1 << bits_per_pixel;
		} 
	}

	/* Number of BBC Micro pixels per byte on current sprite sheet. */
	public int pixels_per_byte { 
		get { 
			return 8 / bits_per_pixel;
		}
	}

	/**
	 * A Vala named constructor as, currently, a screen mode must be supplied.
	 *
	 * @param mode BBC Micro screen mode for all sprites on this sprite sheet.
	 */
	public SpriteSheet.with_mode (int mode) {
		this.mode = mode;
		default_palette = new BeebPalette.Colour[num_colours];

		/* Generate default palette for the BBC Micro screen mode.*/
		switch (num_colours) {
			case 2:
				default_palette[0] = BeebPalette.Colour.BLACK;
				default_palette[1] = BeebPalette.Colour.WHITE;
				break;
			case 4:
				default_palette[0] = BeebPalette.Colour.BLACK;
				default_palette[1] = BeebPalette.Colour.RED;
				default_palette[2] = BeebPalette.Colour.YELLOW;
				default_palette[3] = BeebPalette.Colour.WHITE;
				break;
			case 16:
				default_palette[0] = BeebPalette.Colour.BLACK;
				default_palette[1] = BeebPalette.Colour.RED;
				default_palette[2] = BeebPalette.Colour.GREEN;
				default_palette[3] = BeebPalette.Colour.YELLOW;
				default_palette[4] = BeebPalette.Colour.BLUE;
				default_palette[5] = BeebPalette.Colour.MAGENTA;
				default_palette[6] = BeebPalette.Colour.CYAN;
				default_palette[7] = BeebPalette.Colour.WHITE;
				default_palette[8] = BeebPalette.Colour.BLACK;
				default_palette[9] = BeebPalette.Colour.RED;
				default_palette[10] = BeebPalette.Colour.GREEN;
				default_palette[11] = BeebPalette.Colour.YELLOW;
				default_palette[12] = BeebPalette.Colour.BLUE;
				default_palette[13] = BeebPalette.Colour.MAGENTA;
				default_palette[14] = BeebPalette.Colour.CYAN;
				default_palette[15] = BeebPalette.Colour.WHITE;
				break;
		}
	}
}
